/*******************************************************************************
** 文件名称：gm_multi_key.c
** 文件作用：多功能按键
** 编写作者：Tom Free 付瑞彪
** 编写时间：2020-05-27
** 文件备注：此文件是多功能按键的源文件，主要包括API的具体实现和数据定义
**
**       Copyright (c) 2018-2020 付瑞彪 All Rights Reserved
**
**       1 Tab == 4 Spaces     UTF-8     ANSI C Language(C99)
*******************************************************************************/

#include "gm_multi_key.h"
#include "stdio.h"

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

/* 轮询检测间隔时间，单位：ms，一般5-20ms为宜，
 * 太长反应迟钝，太快占用CPU时间 */
#ifndef GM_MULTI_KEY_POLL_INTERVAL
#define GM_MULTI_KEY_POLL_INTERVAL      (5u)
#endif  /* GM_MULTI_KEY_POLL_INTERVAL */

/* 防抖动滴答数量，时间等于上门的数乘以数量，一般在10-20ms为宜，
 * 太少消抖不完全，太长反应迟钝 */
#ifndef GM_MULTI_KEY_DEBOUNCE_TICKS
#define GM_MULTI_KEY_DEBOUNCE_TICKS     (3u)
#endif  /* GM_MULTI_KEY_DEBOUNCE_TICKS */

/* 连击最长间隔时间，超过此时间认为连击被终结，换算成滴答数 */
#ifndef GM_MULTI_KEY_HIT_AGIN_TICKS
#define GM_MULTI_KEY_HIT_AGIN_TICKS     (300u / GM_MULTI_KEY_POLL_INTERVAL)
#endif  /* GM_MULTI_KEY_HIT_AGIN_TICKS */

/* 长按触发时间，超过此时间认为进入长按，换算成滴答数 */
#ifndef GM_MULTI_KEY_LONG_TICKS
#define GM_MULTI_KEY_LONG_TICKS         (1000u / GM_MULTI_KEY_POLL_INTERVAL)
#endif  /* GM_MULTI_KEY_LONG_TICKS */

/* 长按重复触发时间，即隔多长时间触发一次，换算成滴答数 */
#ifndef GM_MULTI_KEY_REPEAT_TRIG_TICKS
#define GM_MULTI_KEY_REPEAT_TRIG_TICKS  (100u / GM_MULTI_KEY_POLL_INTERVAL)
#endif  /* GM_MULTI_KEY_REPEAT_TRIG_TICKS */

/* 连击最大次数，主要是怕数据溢出 */
#ifndef GM_MULTI_KEY_CLICK_COUNT_MAX
#define GM_MULTI_KEY_CLICK_COUNT_MAX    UINT8_MAX
#endif  /* GM_MULTI_KEY_CLICK_COUNT_MAX */

/* 按键状态定义 */
typedef enum _gm_multi_key_fsm_status_t
{
    /* 按键已释放状态 */
    GM_MULTI_KEY_FSM_STATUS_RELEASE,
    /* 按键已按下状态 */
    GM_MULTI_KEY_FSM_STATUS_PRESS,
    /* 按键松开，等待再次按下，用于判断是否连击 */
    GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN,
    /* 按键再次按下状态，此时不能再响应长按，
     * 所以和GM_MULTI_KEY_FSM_STATUS_PRESS处理不同 */
    GM_MULTI_KEY_FSM_STATUS_PRESS_AGIN,
    /* 等待按键释放状态 */
    GM_MULTI_KEY_FSM_STATUS_WAIT_RELEASE,
    /* 总状态数量 */
    GM_MULTI_KEY_FSM_STATUS_NUM,
} gm_multi_key_fsm_status_t;

/* 按键管理器 */
typedef struct _gm_multi_key_mgr_t
{
    /* 按键链表头节点 */
    gm_multi_key_t *p_list_head;
    /* 链表长度，仅用于统计节点个数 */
    uint16_t        list_length;
} gm_multi_key_mgr_t;

/* 执行事件回调 */
#define GM_MULTI_KEY_EVENT_PROC(p_key)          \
    do                                          \
    {                                           \
        if (p_key->event_proc_cb != NULL)       \
        {                                       \
            p_key->event_proc_cb(p_key);        \
        }                                       \
    } while (0)

/* 管理器参数，为了防止编译器不支持C99，所以采用标准的初始化 */
static gm_multi_key_mgr_t multi_key_mgr =
{
    /* .p_list_head = */ NULL,
    /* .list_length */   0,
};

/* 按键管理器初始化 */
void gm_multi_key_mgr_init(void)
{
    /* 初始化头指针为空 */
    multi_key_mgr.p_list_head = NULL;
}

/* 按键对象初始化 */
gm_multi_key_result_t gm_multi_key_init(gm_multi_key_t *p_key,
                                        gm_multi_key_read_io_cb_t *read_io_cb,
                                        gm_multi_key_event_cb_t *event_cb)
{
    if ((p_key == NULL) ||
        (read_io_cb == NULL) ||
        (event_cb == NULL))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    /* 初始化参数 */
    p_key->next = NULL;
    p_key->read_io_cb = read_io_cb;
    p_key->event_proc_cb = event_cb;
    p_key->click_cnt_max = GM_MULTI_KEY_CLICK_COUNT_MAX;
    p_key->debounce_ticks = GM_MULTI_KEY_DEBOUNCE_TICKS;
    p_key->hit_again_ticks = GM_MULTI_KEY_HIT_AGIN_TICKS;
    p_key->long_press_ticks = GM_MULTI_KEY_LONG_TICKS;
    p_key->long_repeat_ticks = GM_MULTI_KEY_REPEAT_TRIG_TICKS;
    p_key->internal_cnt = 0;
    p_key->debounce_cnt = 0;
    p_key->click_cnt = 0;
    p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
    p_key->event = GM_MULTI_KEY_EVENT_NONE;

    /* 读取一次电平并保存 */
    p_key->level = p_key->read_io_cb(p_key);

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置读取IO回调 */
gm_multi_key_result_t gm_multi_key_set_read_io_cb(gm_multi_key_t *p_key,
                                        gm_multi_key_read_io_cb_t *read_io_cb)
{
    if ((p_key == NULL) ||
        (read_io_cb == NULL))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->read_io_cb = read_io_cb;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置事件处理回调 */
gm_multi_key_result_t gm_multi_key_set_event_cb(gm_multi_key_t *p_key,
                                        gm_multi_key_event_cb_t *event_cb)
{
    if ((p_key == NULL) ||
        (event_cb == NULL))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->event_proc_cb = event_cb;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置连击最大次数 */
gm_multi_key_result_t gm_multi_key_set_click_cnt_max(gm_multi_key_t *p_key,
                                        uint8_t click_cnt_max)
{
    if ((p_key == NULL) ||
        (click_cnt_max == 0))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->click_cnt_max = click_cnt_max;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置防抖周期 */
gm_multi_key_result_t gm_multi_key_set_debounce_ticks(gm_multi_key_t *p_key,
                                        uint8_t debounce_ticks)
{
    if ((p_key == NULL) ||
        (debounce_ticks == 0))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->debounce_ticks = debounce_ticks;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置连击最大间隔时间周期 */
gm_multi_key_result_t gm_multi_key_set_hit_again_ticks(gm_multi_key_t *p_key,
                                        uint8_t hit_again_ticks)
{
    if ((p_key == NULL) ||
        (hit_again_ticks == 0))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->hit_again_ticks = hit_again_ticks;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置长按触发时间周期 */
gm_multi_key_result_t gm_multi_key_set_long_press_ticks(gm_multi_key_t *p_key,
                                        uint8_t long_press_ticks)
{
    if ((p_key == NULL) ||
        (long_press_ticks == 0))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->long_press_ticks = long_press_ticks;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 设置长按重复按下事件触发时间周期 */
gm_multi_key_result_t gm_multi_key_set_long_repeat_ticks(gm_multi_key_t *p_key,
                                        uint8_t long_repeat_ticks)
{
    if ((p_key == NULL) ||
        (long_repeat_ticks == 0))
    {
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    p_key->long_repeat_ticks = long_repeat_ticks;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 添加按键对象到按键链表中 */
gm_multi_key_result_t gm_multi_key_add(gm_multi_key_t* p_key)
{
    gm_multi_key_t* target;

    if (p_key == NULL)
    {
        /* 指针非法，返回参数错误 */
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    /* 搜寻是否已存在于链表中 */
    target = multi_key_mgr.p_list_head;
    while (target != NULL)
    {
        if (target == p_key)
        {
            /* 已存在 */
            return GM_MULTI_KEY_RESULT_EXIST;
        }
        target = target->next;
    }
    /* 未搜索到，添加到头部 */
    p_key->next = multi_key_mgr.p_list_head;
    /* 更新头节点 */
    multi_key_mgr.p_list_head = p_key;
    /* 链表数量加1 */
    multi_key_mgr.list_length++;

    return GM_MULTI_KEY_RESULT_OK;
}

/* 从按键链表中移除按键对象 */
gm_multi_key_result_t gm_multi_key_remove(gm_multi_key_t* p_key)
{
    gm_multi_key_t* ptemp;
    gm_multi_key_t* prev;

    if (p_key == NULL)
    {
        /* 指针非法 */
        return GM_MULTI_KEY_RESULT_ARGS_ERR;
    }

    if ((multi_key_mgr.list_length == 0) ||
        (multi_key_mgr.p_list_head == NULL))
    {
        /* 链表空，未搜索到 */
        return GM_MULTI_KEY_RESULT_NOT_FOUND;
    }

    if (multi_key_mgr.p_list_head == p_key)
    {
        /* 如果需要删除的节点位于链表头，需要更新链表头指针 */
        multi_key_mgr.p_list_head = multi_key_mgr.p_list_head->next;
        /* 保证删除节点的指针安全 */
        p_key->next = NULL;

        return GM_MULTI_KEY_RESULT_OK;
    }

    /* 从第二个开始搜索 */
    prev = multi_key_mgr.p_list_head;
    ptemp = multi_key_mgr.p_list_head->next;
    for (; ptemp != NULL; ptemp = ptemp->next)
    {
        if (ptemp == p_key)
        {
            /* 搜索到，删除节点 */
            prev->next = ptemp->next;
            /* 保证删除节点的指针安全 */
            ptemp->next = NULL;

            return GM_MULTI_KEY_RESULT_OK;
        }
        prev = ptemp;
    }

    /* 未搜索到 */
    return GM_MULTI_KEY_RESULT_NOT_FOUND;
}

/* 轮询一个按键 */
static void gm_multi_key_poll_one_key(gm_multi_key_t* p_key)
{
    /* 读取电平 */
    gm_multi_key_level_t key_level = p_key->read_io_cb(p_key);

    /* 消抖 */
    if (key_level != p_key->level)
    {
        if (++(p_key->debounce_cnt) >= p_key->debounce_ticks)
        {
            p_key->level = key_level;
            p_key->debounce_cnt = 0;
        }
    }
    else
    {
        p_key->debounce_cnt = 0;
    }

    /* 默认按键无事件 */
    p_key->event = GM_MULTI_KEY_EVENT_NONE;

    /* 状态机 */
    switch (p_key->fsm_status)
    {
    case GM_MULTI_KEY_FSM_STATUS_RELEASE:
        if (p_key->level == GM_MULTI_KEY_LEVEL_PRESS)
        {
            /* 按键按下事件 */
            p_key->event = GM_MULTI_KEY_EVENT_PRESS;
            /* 清除计数器 */
            p_key->internal_cnt = 0;
            /* 清除连击计数 */
            p_key->click_cnt = 1;
            /* 切换到按键按下状态 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_PRESS;
        }
        break;

    case GM_MULTI_KEY_FSM_STATUS_PRESS:
        if (p_key->level != GM_MULTI_KEY_LEVEL_PRESS)
        {
            /* 未达到长按时间之前抬起，按键松开事件 */
            p_key->event = GM_MULTI_KEY_EVENT_RELEASE;
            /* 清除计数器 */
            p_key->internal_cnt = 0;
            /* 切换到等待连击状态 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN;
        }
        else if (++(p_key->internal_cnt) > p_key->long_press_ticks)
        {
            /* 超过长按时间还未松开，首次触发长按事件 */
            p_key->event = GM_MULTI_KEY_EVENT_LONG_FIRST;
            /* 切换到等待按键松开状态 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_WAIT_RELEASE;
        }
        break;

    case GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN:
        if (p_key->level == GM_MULTI_KEY_LEVEL_PRESS)
        {
            /* 按键按下事件 */
            p_key->event = GM_MULTI_KEY_EVENT_PRESS;
            /* 连击计数加1 */
            if (p_key->click_cnt < p_key->click_cnt_max)
            {
                p_key->click_cnt++;
            }
            /* 清除计数器 */
            p_key->internal_cnt = 0;
            /* 切换到按键再次按下状态 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_PRESS_AGIN;
        }
        else if (++(p_key->internal_cnt) > p_key->hit_again_ticks)
        {
            /* 超过连击间隔时间还未再次按下按键 */
            if (p_key->click_cnt == 1)
            {
                /* 仅点击了一次 */
                p_key->event = GM_MULTI_KEY_EVENT_SINGLE_CLICK;
            }
            else if (p_key->click_cnt == 2)
            {
                /* 双击 */
                p_key->event = GM_MULTI_KEY_EVENT_DOUBLE_CLICK;
            }
            else
            {
                /* 多次连击 */
                p_key->event = GM_MULTI_KEY_EVENT_MULTI_CLICK;
            }
            /* 切换到按键抬起状态，此次按键流程结束 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
        }
        break;

    case GM_MULTI_KEY_FSM_STATUS_PRESS_AGIN:
        if (p_key->level != GM_MULTI_KEY_LEVEL_PRESS)
        {
            /* 按键松开事件 */
            p_key->event = GM_MULTI_KEY_EVENT_RELEASE;
            /* 清除计数器 */
            p_key->internal_cnt = 0;
            /* 切换到等待按键再次按下状态 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN;
        }
        break;

    case GM_MULTI_KEY_FSM_STATUS_WAIT_RELEASE:
        if (p_key->level == GM_MULTI_KEY_LEVEL_PRESS)
        {
            if (++(p_key->internal_cnt) > p_key->long_repeat_ticks)
            {
                /* 清除计数 */
                p_key->internal_cnt = 0;
                /* 继续按下，按键重复按下事件 */
                p_key->event = GM_MULTI_KEY_EVENT_REPEAT;
            }
        }
        else
        {
            /* 按键松开，按键抬起事件 */
            p_key->event = GM_MULTI_KEY_EVENT_RELEASE;
            /* 清除计数器 */
            p_key->internal_cnt = 0;
            /* 清除连击计数 */
            p_key->click_cnt = 0;
            /* 回到释放状态 */
            p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
        }
        break;

    default:
        /* 异常，回到释放状态 */
        p_key->internal_cnt = 0;
        p_key->click_cnt = 0;
        p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
        break;
    }

    /* 有事件产生，执行回调 */
    if (p_key->event != GM_MULTI_KEY_EVENT_NONE)
    {
        GM_MULTI_KEY_EVENT_PROC(p_key);
    }
}

/* 轮询所有按键 */
void gm_multi_key_poll(void)
{
    gm_multi_key_t* p_key;

    for (p_key = multi_key_mgr.p_list_head; p_key != NULL; p_key = p_key->next)
    {
        gm_multi_key_poll_one_key(p_key);
    }
}

#ifdef __cplusplus
}
#endif  /* __cplusplus */
